using PeDALS.Tools.AZ.PSTArchive.OutputData;
using PeDALS.Tools.AZ.PSTArchive.PSTData;
//using PeDALS.Tools.AZ.PSTArchive.TransformData;
using System;
using System.Collections;                   //***???
using System.Collections.Generic;
using System.Collections.Specialized;       //***???
using System.ComponentModel;
using System.Diagnostics;
using System.IO;                            //***???
using System.IO.IsolatedStorage;            //***???
using System.Reflection;
using System.Text;
using System.Threading;
using System.Xml;
using System.Xml.Serialization;

namespace PeDALS.Tools.AZ.PSTArchive.FileEngine
{
    public partial class Extract : Component, iPlug
    {
        /////////////////////////////////////////////////////////////
        #region Private variables

        private bool mblnNewExport;
        private bool mblnWriteAttachmentBinary;
        private bool mblnWriteMessageBinary;
        private int mintItemsProcessed;
        private int mintItemsCount;
        private int mintTaskItemsProcessed;
        private int mintTaskItemsCount;
        private int mintMessagesExported;
        private int mintFoldersExported;
        private string mstrCurrentOperation;
        private string mstrExportFolder;
        private string mstrPSTFilename;
        private Stopwatch mswTime = new Stopwatch();
        private System.Timers.Timer mtTimer = new System.Timers.Timer(60000);

        private delegate void WorkerEventHandler(string strPSTFilename, AsyncOperation aoOperation);

        private SendOrPostCallback onStart;
        private SendOrPostCallback onProgress;
        private SendOrPostCallback onComplete;
        private SendOrPostCallback onException;

        private HybridDictionary mhdStateDictionary =
            new HybridDictionary();

        #endregion

        /////////////////////////////////////////////////////////////
        #region Public properties

        #region iPlug Properties

        #region Metadata

        /// <summary>
        /// The name of the plug-in.
        /// </summary>
        public string Name
        {
            get
            {
                return Assembly.GetExecutingAssembly().GetName().Name;
            }
        }
        /// <summary>
        /// The description of the plug-in.
        /// </summary>
        public string Description
        {
            get
            {
                return string.Format("{0} - {1}", FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).Comments, Version);
            }
        }
        /// <summary>
        /// Boolean to indicate if a profile definition is required for the plug-in.
        /// </summary>
        public bool ProfileRequired
        {
            get
            {
                return false;
            }
        }
        /// <summary>
        /// The default PST profile for the plug-in.
        /// </summary>
        public string DefaultProfile
        {
            get
            {
                return "";
            }
        }
        /// <summary>
        /// The version of the plug-in.
        /// </summary>
        public string Version
        {
            get
            {
                return string.Format("v. {0} {1}", FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion, FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).ProductVersion);
            }
        }
        /// <summary>
        /// The author of the plug-in.
        /// </summary>
        public string Author
        {
            get
            {
                return string.Format("{0}", FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).CompanyName);
            }
        }

        #endregion Metadata

        #endregion iPlug Properties

        #endregion

        /////////////////////////////////////////////////////////////
        #region Public events

        #region iPlug Events
        public event ExecutionProgressEventHandler ExecutionProgress;
        public event ExecutionCompleteEventHandler ExecutionComplete;
        public event ExecutionExceptionEventHandler ExecutionException;
        #endregion iPlug Events

        #endregion

        /////////////////////////////////////////////////////////////
        #region Construction

        public Extract()
        {
            InitializeComponent();
            InitializeDelegates();
        }

        public Extract(IContainer icContainer)
        {
            icContainer.Add(this);

            InitializeComponent();
            InitializeDelegates();
        }

        protected virtual void InitializeDelegates()
        {
            onProgress =
                new SendOrPostCallback(PostProgress);
            onComplete =
                new SendOrPostCallback(PostComplete);
            onException =
                new SendOrPostCallback(PostException);
        }

        #endregion Construction

        /////////////////////////////////////////////////////////////
        #region Internal events

        // This method is invoked via the AsyncOperation object,
        // so it is guaranteed to be executed on the correct thread.
        private void PostProgress(object oState)
        {
            ProgressEventArgs peaArgs =
                oState as ProgressEventArgs;

            OnExecutionProgress(peaArgs);
        }

        protected virtual void OnExecutionProgress(ProgressEventArgs peaArgs)
        {
            if (ExecutionProgress != null)
            {
                ExecutionProgress(this, peaArgs);
            }
        }

        // This method is invoked via the AsyncOperation object,
        // so it is guaranteed to be executed on the correct thread.
        private void PostComplete(object oState)
        {
            CompleteEventArgs ceaArgs =
                oState as CompleteEventArgs;

            OnExecutionComplete(ceaArgs);
        }

        protected virtual void OnExecutionComplete(CompleteEventArgs ceaArgs)
        {
            if (ExecutionComplete != null)
            {
                ExecutionComplete(this, ceaArgs);
            }
        }

        // This method is invoked via the AsyncOperation object,
        // so it is guaranteed to be executed on the correct thread.
        private void PostException(object oState)
        {
            ProgressEventArgs peaArgs =
                oState as ProgressEventArgs;

            OnExecutionException(peaArgs);
        }

        protected virtual void OnExecutionException(ProgressEventArgs peaArgs)
        {
            if (ExecutionException != null)
            {
                ExecutionException(this, peaArgs);
            }
        }

        #endregion

        /////////////////////////////////////////////////////////////
        #region Public Methods

        #region iPlug Methods

        /// <summary>
        /// Executes the plug-in.
        /// </summary>
        /// <returns>void</returns>
        public void Execute(string strPSTFilename, string strExportFolder, string strProfile, bool blnWriteMessageBinary, bool blnWriteAttachmentBinary)
        {
            mswTime.Reset();
            mswTime.Start();
            mintTaskItemsProcessed = 0;
            mintTaskItemsCount = 1;
            mintItemsProcessed = 0;
            mintItemsCount = 10000;
            mintMessagesExported = 0;
            mintFoldersExported = 0;
            mstrCurrentOperation = string.Format("initializing export of {0}", strPSTFilename);
            OnExecutionProgress(new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Log, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), null));
            mtTimer.Elapsed += new System.Timers.ElapsedEventHandler(TimerElapsed);
            mstrExportFolder = strExportFolder;
            mstrPSTFilename = strPSTFilename;
            mblnWriteAttachmentBinary = blnWriteAttachmentBinary;
            mblnWriteMessageBinary = blnWriteMessageBinary;
            mblnNewExport = true;

            try
            {
                Guid gTask = InitializeAsync();
                WorkerEventHandler wehHandler = new WorkerEventHandler(EnumerateFolders);
                wehHandler.BeginInvoke(mstrPSTFilename, (AsyncOperation)mhdStateDictionary[gTask], new AsyncCallback(ExecuteCallback), gTask);
            }
            catch (System.Exception eException)
            {
                OnExecutionException(new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Exception, string.Format("Error:  Execution failure {0} as a result of {1}", mstrCurrentOperation, eException.Message), ((mintItemsProcessed * 100) / mintItemsCount), null));
                if (mhdStateDictionary.Count > 0)
                {
                    Cancel();
                }
                else
                {
                    mswTime.Stop();
                    OnExecutionComplete(new CompleteEventArgs(mintMessagesExported, mintFoldersExported, LogEntryType.Exception, string.Format("Execution aborted by exception while {0}", mstrCurrentOperation), mswTime.Elapsed, null, false, null));
                }
            }
        }

        /// <summary>
        /// Cancels the execution of a plug-in
        /// </summary>
        /// <returns>void</returns>
        public void Cancel()
        {
        }

        #endregion iPlugMethods

        // This method cancels a pending asynchronous operation.
        public void CancelAsync(object gTask)
        {
            AsyncOperation aoOperation = (AsyncOperation)mhdStateDictionary[gTask];
            if (aoOperation != null)
            {
                lock (mhdStateDictionary.SyncRoot)
                {
                    mhdStateDictionary.Remove(gTask);
                }
            }
        }

        #endregion Public Methods

        /////////////////////////////////////////////////////////////
        #region Private methods

        private void ExecuteCallback(IAsyncResult iarResult)
        {
            Guid gTask = (Guid)iarResult.AsyncState;
            AsyncOperation aoOperation = (AsyncOperation)mhdStateDictionary[gTask];
            lock (mhdStateDictionary.SyncRoot)
            {
                mhdStateDictionary.Remove(gTask);
            }

            //*** Do something here
            mswTime.Stop();
            aoOperation.PostOperationCompleted(this.PostComplete, new CompleteEventArgs(mintMessagesExported, mintFoldersExported, LogEntryType.Log, "Completed (Maybe put errors and warnings here?)", mswTime.Elapsed, null, false, null));
        }

        private void TimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            //aoOperation.Post(this.PostProgress, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
            OnExecutionProgress(new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), null));
        }

        private void EnumerateFolders(string strPSTFilename, AsyncOperation aoOperation)
        {
            bool blnNewFolder = true;
            bool blnUnicode = false;

            StringBuilder sbStringGen = new StringBuilder();
            mstrCurrentOperation = string.Format("loading property configuration file {0}{1}", Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "\\property.xml");
            string strExportFolder = "";
            FileStream fsStream = null;
            BinaryReader brReader = null;
            SortedList<UInt32, DynProperty> mslProps = new SortedList<uint, DynProperty>();
            IntProperty ipProperties;
            if (!TaskCanceled(aoOperation.UserSuppliedState))
            {
                try
                {
                    aoOperation.Post(this.PostProgress, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    if (!DynProps.Loaded)
                    {
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Log, string.Format("Error:  Execution failure {0} as a result of {1}", mstrCurrentOperation, DynProps.Error), ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }

                    mstrCurrentOperation = string.Format("opening file {0}", mstrPSTFilename);
                    fsStream = new FileStream(mstrPSTFilename, FileMode.Open);
                    brReader = new BinaryReader(fsStream);
                    brReader.BaseStream.Seek(0, SeekOrigin.Begin);
                    mstrCurrentOperation = string.Format("reading {0} header information", mstrPSTFilename);
                    PSTHeader phHeader = new PSTHeader(brReader);
                    CheckHeader(phHeader, brReader.BaseStream.Length, aoOperation);
                    mstrCurrentOperation = string.Format("loading {0} necessary header information", mstrPSTFilename);
                    blnUnicode = phHeader.Unicode;
                    UInt64 u64NextPageBID = phHeader.BIDrawNextPage;
                    UInt64 u64NextBID = phHeader.BIDrawNext;
                    UInt64 u64BIDrawNodeBT = phHeader.BIDrawNodeBT;
                    long lngNodeBTreeAddress = phHeader.lngNodeBTAddress;
                    UInt64 u64BIDrawBlockBT = phHeader.BIDrawBlockBT;
                    long lngBlockBTreeAddress = phHeader.lngBlockBTAddress;
                    byte[] rgbFreeMap = phHeader.rgbFreeMap;
                    byte[] rgbFreePageMap = phHeader.rgbFreePageMap;
                    byte bCryptMethod = phHeader.bCryptMethod < 0x03 ? phHeader.bCryptMethod : (byte) 0x00;
                    phHeader = null;
                    byte[] baReserved = blnUnicode ? brReader.ReadBytes(16332) : brReader.ReadBytes(16384);
                    PSTDensityList pdlDensityList = new PSTDensityList(brReader, blnUnicode);
                    CheckMaps(brReader, blnUnicode, rgbFreeMap, rgbFreePageMap);
                    rgbFreeMap = null;
                    rgbFreePageMap = null;

                    mstrCurrentOperation = string.Format("loading block entries");
                    aoOperation.Post(this.PostProgress, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    PSTBTreePage pmRootBlockBTree = new PSTBTreePage(brReader, 0, u64BIDrawBlockBT, lngBlockBTreeAddress, PageTypes.BlockBTree, 0xFF, blnUnicode, "BlockRoot", lngBlockBTreeAddress);
                    SortedList<UInt64, TreeBlockEntry> sltbeBlockEntries = LoadBlockEntries(pmRootBlockBTree);
                    mstrCurrentOperation = string.Format("completed loading {0} block entries", sltbeBlockEntries.Count);
                    aoOperation.Post(this.PostProgress, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Log, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));

                    mstrCurrentOperation = string.Format("loading node entries");
                    aoOperation.Post(this.PostProgress, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    PSTBTreePage pmRootNodeBTree = new PSTBTreePage(brReader, 0, u64BIDrawNodeBT, lngNodeBTreeAddress, PageTypes.NodeBTree, 0xFF, blnUnicode, "NodeRoot", lngNodeBTreeAddress);
                    SortedList<UInt64, TreeNodeEntry> sltneNodeEntries = LoadNodeEntries(pmRootNodeBTree);
                    mstrCurrentOperation = string.Format("completed loading {0} node entries", sltneNodeEntries.Count);
                    aoOperation.Post(this.PostProgress, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Log, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));

                    long lngCurrentTotalMemory;

                    /////////////////////////////////////////////////////////////
                    #region Named Properties Code - Node 0x61

                    /// this stuff is for named properties as defined in node 0x61
                    //if (sltneNodeEntries.ContainsKey(0x61))
                    //{
                    //    mstrCurrentOperation = string.Format("loading named properties node");
                    //    aoOperation.Post(this.PostProgress, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    //    TreeNodeEntry tneNamedPropertyNode = sltneNodeEntries[0x61];
                    //    lngCurrentTotalMemory = tneNamedPropertyNode.LoadData(brReader, blnUnicode, sltbeBlockEntries, bCryptMethod);
                    //    if (tneNamedPropertyNode.Heaps.ContainsKey(1))
                    //    {
                    //        if (tneNamedPropertyNode.Heaps[1].Properties != null)
                    //        {
                    //            List<Guid> lgGUIDEntries = new List<Guid>();
                    //            List<NameEntry> lneNameEntries = new List<NameEntry>();
                    //            SortedList<UInt32, PropertyValue> slpvProperties = tneNamedPropertyNode.Heaps[1].Properties;
                    //            if (slpvProperties.ContainsKey(0x00020102))
                    //            {
                    //                MemoryStream msGUIDEntries = new MemoryStream((byte[])slpvProperties[0x00020102].Value);
                    //                BinaryReader brGUIDEntriesReader = new BinaryReader(msGUIDEntries);

                    //                do
                    //                {
                    //                    lgGUIDEntries.Add(new Guid(brGUIDEntriesReader.ReadBytes(16)));
                    //                } while (brGUIDEntriesReader.BaseStream.Position < brGUIDEntriesReader.BaseStream.Length);

                    //            }
                    //            if (slpvProperties.ContainsKey(0x00030102))
                    //            {
                    //                MemoryStream msNameEntries = new MemoryStream((byte[])slpvProperties[0x00030102].Value);
                    //                BinaryReader brNameEntriesReader = new BinaryReader(msNameEntries);

                    //                do
                    //                {
                    //                    lneNameEntries.Add(new NameEntry(brNameEntriesReader.ReadUInt32(), brNameEntriesReader.ReadUInt16(), brNameEntriesReader.ReadUInt16(), lgGUIDEntries));
                    //                } while (brNameEntriesReader.BaseStream.Position < brNameEntriesReader.BaseStream.Length);

                    //            }
                    //            int intI = 0;
                    //            intI = intI;
                    //        }
                    //    }
                    //}
                    #endregion

                    SortedList<NIDTypes, List<UInt64>> slNodeTypeCounts = new SortedList<NIDTypes, List<UInt64>>();
                    List<UInt64> lu64NIDraw = new List<UInt64>();
                    lu64NIDraw.AddRange(sltneNodeEntries.Keys);
                    foreach (NIDTypes ntType in Enum.GetValues(typeof(NIDTypes)))
                    {
                        List<UInt64> lu64FoundNIDrawEntries = lu64NIDraw.FindAll(delegate(UInt64 u64FoundNIDraw) { return (NIDTypes)(u64FoundNIDraw & 0x1f) == ntType; });
                        if (lu64FoundNIDrawEntries.Count > 0)
                        {
                            slNodeTypeCounts.Add(ntType, lu64FoundNIDrawEntries);
                        }
                    }
                    List<UInt64> lu64NormalFolderNIDraws = slNodeTypeCounts[NIDTypes.NormalFolder];
                    int intFolderCount = lu64NormalFolderNIDraws.Count;
                    List<UInt64> lu64NormalMessageNIDraws = slNodeTypeCounts[NIDTypes.NormalMessage];
                    int intItemCount = lu64NormalMessageNIDraws.Count;
                    mintItemsCount = intFolderCount + intItemCount;
                    slNodeTypeCounts.Clear();
                    slNodeTypeCounts = null;
                    mstrCurrentOperation = string.Format("found {0} items in {1} folders", intItemCount, intFolderCount);
                    aoOperation.Post(this.PostProgress, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Log, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));

                    UInt64 u64CurrentNIDraw = 0;
                    SortedList<UInt64, FolderStats> slfsFolders = new SortedList<UInt64, FolderStats>();
                    SortedList<UInt64, FolderStats> slfsEmptyFolders = new SortedList<UInt64, FolderStats>();
                    mintTaskItemsProcessed = 0;
                    mintTaskItemsCount = lu64NormalFolderNIDraws.Count;
                    foreach (UInt64 u64NormalFolderNIDraw in lu64NormalFolderNIDraws)
                    {
                        if (slfsFolders.ContainsKey(u64CurrentNIDraw))
                        {
                            if ((!slfsFolders[u64CurrentNIDraw].OnlyNormalSubfolders | slfsFolders[u64CurrentNIDraw].Subfolders.Count < 1) & (slfsFolders[u64CurrentNIDraw].ContentCount < 1) & (slfsFolders[u64CurrentNIDraw].HiddenCount < 1) & (slfsFolders[u64CurrentNIDraw].Messages.Count < 1))
                            {
                                slfsEmptyFolders.Add(u64CurrentNIDraw, slfsFolders[u64CurrentNIDraw]);
                                slfsFolders.Remove(u64CurrentNIDraw);
                            }
                        }
                        u64CurrentNIDraw = u64NormalFolderNIDraw;
                        List<UInt64> lu64FoundFolderNIDraws = lu64NIDraw.FindAll(delegate(UInt64 u64FoundNIDraw) { return (u64FoundNIDraw >> 5) == (u64NormalFolderNIDraw >> 5); });
                        foreach (UInt64 u64FolderNIDraw in lu64FoundFolderNIDraws)
                        {
                            if (sltneNodeEntries.ContainsKey(u64FolderNIDraw))
                            {
                                TreeNodeEntry tneFolderNode = sltneNodeEntries[u64FolderNIDraw];
                                UInt64 u64NIDrawParent = tneFolderNode.NIDrawParent;
                                lngCurrentTotalMemory = tneFolderNode.LoadData(brReader, blnUnicode, sltbeBlockEntries, bCryptMethod);
                                switch (tneFolderNode.NIDType)
                                {
                                    case NIDTypes.NormalFolder:
                                        if (tneFolderNode.Heaps.ContainsKey((UInt32)NIDTypes.NormalFolder))
                                        {
                                            if (tneFolderNode.Heaps[(UInt32)NIDTypes.NormalFolder].Context == ContextType.Property)
                                            {
                                                SortedList<UInt32, PropertyValue> slPropertyValues = tneFolderNode.Heaps[(UInt32)NIDTypes.NormalFolder].Properties;
                                                string strName = slPropertyValues.ContainsKey(0x3001001f) ? Convert.ToString(slPropertyValues[0x3001001f].Value) : slPropertyValues.ContainsKey(0x3001001e) ? Convert.ToString(slPropertyValues[0x3001001e].Value) : "";
                                                mstrCurrentOperation = string.Format("examining folder {0}", strName);
                                                aoOperation.Post(this.PostProgress, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                                                string strPath = string.Format("{0}{1}", slfsFolders.ContainsKey(u64NIDrawParent) ? string.Format("{0}{1}", slfsFolders[u64NIDrawParent].Path, strName) : "", "\\");
                                                int intContentCount = slPropertyValues.ContainsKey(0x36020003) ? Convert.ToInt32(slPropertyValues[0x36020003].Value) : 0;
                                                int intHiddenCount = slPropertyValues.ContainsKey(0x66350003) ? Convert.ToInt32(slPropertyValues[0x66350003].Value) : 0;
                                                bool bSubfoldersExist = slPropertyValues.ContainsKey(0x360a000b) ? Convert.ToBoolean(slPropertyValues[0x360a000b].Value) : false;
                                                FolderStats fsFolder = new FolderStats(u64CurrentNIDraw, strName, strPath, intContentCount, intHiddenCount, bSubfoldersExist);
                                                if ((intContentCount > 0) | (intHiddenCount > 0) | bSubfoldersExist)
                                                {
                                                    slfsFolders.Add(u64CurrentNIDraw, fsFolder);
                                                    if (slfsFolders.ContainsKey(u64NIDrawParent) & (u64NIDrawParent != u64CurrentNIDraw))
                                                    {
                                                        if (slfsFolders[u64NIDrawParent].Subfolders.ContainsKey(u64CurrentNIDraw))
                                                        {
                                                            slfsFolders[u64NIDrawParent].Subfolders.Remove(u64CurrentNIDraw);
                                                        }
                                                        else
                                                        {
                                                            mstrCurrentOperation = string.Format("Warning:  Subfolder {0}({1}) not found in parent folder {2}({3})", fsFolder.Name, fsFolder.Path, slfsFolders[u64NIDrawParent].Name, slfsFolders[u64NIDrawParent].Path);
                                                            aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Warning, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                                                        }
                                                    }
                                                }
                                                else
                                                {
                                                    slfsEmptyFolders.Add(u64CurrentNIDraw, fsFolder);
                                                }
                                            }
                                        }
                                        break;
                                    case NIDTypes.HierarchyTable:
                                        if (tneFolderNode.Heaps.ContainsKey((UInt32)NIDTypes.HierarchyTable))
                                        {
                                            if (tneFolderNode.Heaps[(UInt32)NIDTypes.HierarchyTable].Context == ContextType.Table)
                                            {
                                                SortedList<UInt32, SortedList<UInt32, PropertyValue>> slslPropertyValues = tneFolderNode.Heaps[(UInt32)NIDTypes.HierarchyTable].Table;
                                                foreach (SortedList<UInt32, PropertyValue> slPropertyValue in slslPropertyValues.Values)
                                                {
                                                    UInt64 u64NIDraw = slPropertyValue.ContainsKey(0x67f20003) ? Convert.ToUInt64(slPropertyValue[0x67f20003].Value) : 0;
                                                    string strName = slPropertyValue.ContainsKey(0x3001001f) ? Convert.ToString(slPropertyValue[0x3001001f].Value) : slPropertyValue.ContainsKey(0x3001001e) ? Convert.ToString(slPropertyValue[0x3001001e].Value) : "";
                                                    string strPath = "";
                                                    int intContentCount = slPropertyValue.ContainsKey(0x36020003) ? Convert.ToInt32(slPropertyValue[0x36020003].Value) : 0;
                                                    int intHiddenCount = slPropertyValue.ContainsKey(0x66350003) ? Convert.ToInt32(slPropertyValue[0x66350003].Value) : 0;
                                                    bool bSubfoldersExist = slPropertyValue.ContainsKey(0x360a000b) ? Convert.ToBoolean(slPropertyValue[0x360a000b].Value) : false;
                                                    FolderStats fsFolder = new FolderStats(u64NIDraw, strName, strPath, intContentCount, intHiddenCount, bSubfoldersExist);
                                                    if ((intContentCount > 0) | (intHiddenCount > 0) | bSubfoldersExist)
                                                    {
                                                        slfsFolders[u64CurrentNIDraw].Subfolders.Add(u64NIDraw, fsFolder);
                                                    }
                                                    else
                                                    {
                                                        slfsFolders[u64CurrentNIDraw].EmptySubfolders.Add(u64NIDraw, fsFolder);
                                                    }
                                                }
                                            }
                                        }
                                        break;
                                    case NIDTypes.ContentsTable:
                                        if (tneFolderNode.Heaps.ContainsKey((UInt32)NIDTypes.ContentsTable))
                                        {
                                            if (tneFolderNode.Heaps[(UInt32)NIDTypes.ContentsTable].Context == ContextType.Table)
                                            {
                                                SortedList<UInt32, SortedList<UInt32, PropertyValue>> slslPropertyValues = tneFolderNode.Heaps[(UInt32)NIDTypes.ContentsTable].Table;
                                                foreach (SortedList<UInt32, PropertyValue> slPropertyValue in slslPropertyValues.Values)
                                                {
                                                    UInt64 u64NIDraw = slPropertyValue.ContainsKey(0x67f20003) ? Convert.ToUInt64(slPropertyValue[0x67f20003].Value) : 0;
                                                    string strDisplayTo = slPropertyValue.ContainsKey(0x0e04001f) ? Convert.ToString(slPropertyValue[0x0e04001f].Value) : slPropertyValue.ContainsKey(0x0e04001e) ? Convert.ToString(slPropertyValue[0x0e04001e].Value) : "";
                                                    string strMessageClass = slPropertyValue.ContainsKey(0x001a001f) ? Convert.ToString(slPropertyValue[0x001a001f].Value) : slPropertyValue.ContainsKey(0x001a001e) ? Convert.ToString(slPropertyValue[0x001a001e].Value) : "";
                                                    DateTime datMessageDeliveryTime = slPropertyValue.ContainsKey(0x0e060040) ? Convert.ToDateTime(slPropertyValue[0x0e060040].Value) : DateTime.MinValue;
                                                    int intMessageSize = slPropertyValue.ContainsKey(0x0e080003) ? Convert.ToInt32(slPropertyValue[0x0e080003].Value) : 0;
                                                    string strSenderName = slPropertyValue.ContainsKey(0x0c1a001f) ? Convert.ToString(slPropertyValue[0x0c1a001f].Value) : slPropertyValue.ContainsKey(0x0c1a001e) ? Convert.ToString(slPropertyValue[0x0c1a001e].Value) : "";
                                                    string strSubject = slPropertyValue.ContainsKey(0x0037001f) ? Convert.ToString(slPropertyValue[0x0037001f].Value) : slPropertyValue.ContainsKey(0x0037001e) ? Convert.ToString(slPropertyValue[0x0037001e].Value) : "";
                                                    MessageStats msMessage = new MessageStats(u64NIDraw, strDisplayTo, strMessageClass, datMessageDeliveryTime, intMessageSize, strSenderName, strSubject);
                                                    if (!slfsFolders[u64CurrentNIDraw].Messages.ContainsKey(u64NIDraw)) slfsFolders[u64CurrentNIDraw].Messages.Add(u64NIDraw, msMessage);
                                                }
                                            }
                                        }
                                        break;
                                    case NIDTypes.AssociatedContentsTableFAI:
                                        if (tneFolderNode.Heaps.ContainsKey((UInt32)NIDTypes.AssociatedContentsTableFAI))
                                        {
                                            if (tneFolderNode.Heaps[(UInt32)NIDTypes.AssociatedContentsTableFAI].Context == ContextType.Table)
                                            {
                                                SortedList<UInt32, SortedList<UInt32, PropertyValue>> slslPropertyValues = tneFolderNode.Heaps[(UInt32)NIDTypes.AssociatedContentsTableFAI].Table;
                                                foreach (SortedList<UInt32, PropertyValue> slPropertyValue in slslPropertyValues.Values)
                                                {
                                                }
                                            }
                                        }
                                        break;
                                    default:
                                        break;
                                }
                                if (tneFolderNode.ByteData != null)
                                {
                                    tneFolderNode.ByteData.Clear();
                                    tneFolderNode.ByteData = null;
                                }
                                if (tneFolderNode.Heaps != null)
                                {
                                    tneFolderNode.Heaps.Clear();
                                    tneFolderNode.Heaps = null;
                                }
                                tneFolderNode = null;

                                sltneNodeEntries.Remove(u64FolderNIDraw);
                                if (lngCurrentTotalMemory > 536870912) GC.Collect();
                            }
                        }
                        mintTaskItemsProcessed++;
                        mintItemsProcessed++;
                    }
                    int intItemCheckCount = 0;
                    List<FolderStats> lfsFoldersReplica = new List<FolderStats>();
                    lfsFoldersReplica.AddRange(slfsFolders.Values);
                    foreach (FolderStats fsFolder in lfsFoldersReplica)
                    {
                        if ((fsFolder.ContentCount < 1) & (fsFolder.HiddenCount < 1) & (fsFolder.Messages.Count < 1))
                        {
                            slfsEmptyFolders.Add(fsFolder.NIDraw, fsFolder);
                            slfsFolders.Remove(fsFolder.NIDraw);
                        }
                        else
                        {
                            intItemCheckCount += fsFolder.Messages.Count;
                        }
                    }
                    lfsFoldersReplica.Clear();
                    lfsFoldersReplica = null;
                    if (intItemCheckCount == intItemCount)
                    {
                        intFolderCount = slfsFolders.Count;
                        mstrCurrentOperation = string.Format("processing {0} items from {1} exportable folders", intItemCount, intFolderCount);
                        aoOperation.Post(this.PostProgress, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Log, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    else
                    {
                        mstrCurrentOperation = string.Format("Warning:  Item count mismatch; Folder contents tables indicate {0} items; Processing {1} items from {2} exportable folders", intItemCheckCount, intItemCount, intFolderCount);
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Warning, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    mintItemsCount += intFolderCount;

                    foreach (FolderStats fsFolder in slfsFolders.Values)
                    {
                        strExportFolder = string.Format("{0}{1}", mstrExportFolder, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                        DirectoryInfo diDirectory = new DirectoryInfo(strExportFolder);
                        mstrCurrentOperation = string.Format("check export folder {0} existence", strExportFolder);
                        if (!diDirectory.Exists)
                        {
                            //Trace.Write(string.Format("Directory {0} length = {1}", strExportFolder, strExportFolder.Length));
                            mstrCurrentOperation = string.Format("create export folder {0}", mstrExportFolder);
                            diDirectory.Create();
                        }
                        mintTaskItemsProcessed = 0;
                        mintTaskItemsCount = fsFolder.Messages.Count;
                        List<UInt64> lu64MessageKeys = new List<UInt64>();
                        lu64MessageKeys.AddRange(fsFolder.Messages.Keys);
                        foreach (UInt64 u64NormalMessageNIDraw in lu64MessageKeys)
                        {
                            u64CurrentNIDraw = u64NormalMessageNIDraw;
                            List<UInt64> lu64FoundMessageNIDraws = lu64NIDraw.FindAll(delegate(UInt64 u64FoundNIDraw) { return (u64FoundNIDraw >> 5) == (u64NormalMessageNIDraw >> 5); });
                            foreach (UInt64 u64MessageNIDraw in lu64FoundMessageNIDraws)
                            {
                                if (sltneNodeEntries.ContainsKey(u64MessageNIDraw))
                                {
                                    string strExportFileSpec = "";
                                    mstrCurrentOperation = string.Format("exporting folder {0}", fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                    aoOperation.Post(this.PostProgress, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                                    TreeNodeEntry tneMessageNode = sltneNodeEntries[u64MessageNIDraw];
                                    UInt64 u64NIDrawParent = tneMessageNode.NIDrawParent;
                                    mstrCurrentOperation = string.Format("loading node data for message 0x{0:x8}; message {1} of {2} in folder {3}", u64MessageNIDraw, mintTaskItemsProcessed + 1, mintTaskItemsCount, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                    lngCurrentTotalMemory = tneMessageNode.LoadData(brReader, blnUnicode, sltbeBlockEntries, bCryptMethod);
                                    List<UInt32> lu32HeapKeys = new List<UInt32>();
                                    mstrCurrentOperation = string.Format("loading {0} heap keys for message 0x{1:x8}; message {2} of {3} in folder {4}", tneMessageNode.Heaps.Keys.Count, u64MessageNIDraw, mintTaskItemsProcessed + 1, mintTaskItemsCount, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                    lu32HeapKeys.AddRange(tneMessageNode.Heaps.Keys);
                                    switch (tneMessageNode.NIDType)
                                    {
                                        case NIDTypes.NormalMessage:
                                            List<StringBuilder> lsbXML = new List<StringBuilder>();
                                            FileInfo fiFile;
                                            string strExportFile = "";
                                            bool blnValidFileName = false;
                                            foreach (UInt32 u32Key in lu32HeapKeys)
                                            {
                                                if (tneMessageNode.Heaps.ContainsKey(u32Key))
                                                {
                                                    NIDTypes ntType = (NIDTypes)(u32Key & 0x1f);
                                                    switch (ntType)
                                                    {
                                                        case NIDTypes.NormalMessage:
                                                            if (tneMessageNode.Heaps[u32Key].Context == ContextType.Property)
                                                            {
                                                                mstrCurrentOperation = string.Format("loading {0} properties for message 0x{1:x8}; message {2} of {3} in folder {4}", tneMessageNode.Heaps[u32Key].Properties.Count, u64MessageNIDraw, mintTaskItemsProcessed + 1, mintTaskItemsCount, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                                                SortedList<UInt32, PropertyValue> slPropertyValues = tneMessageNode.Heaps[u32Key].Properties;
                                                                if (!blnValidFileName)
                                                                {
                                                                    mstrCurrentOperation = string.Format("creating export message filename for message 0x{0:x8}; message {1} of {2} in folder {3}", u64MessageNIDraw, mintTaskItemsProcessed + 1, mintTaskItemsCount, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                                                    DateTime dtReceived = slPropertyValues.ContainsKey(0x0e060040) ? Convert.ToDateTime(slPropertyValues[0x0e060040].Value) : DateTime.MinValue;
                                                                    blnValidFileName = (dtReceived != DateTime.MinValue);
                                                                    string strType = "";
                                                                    string strClass = slPropertyValues.ContainsKey(0x001a001f) ? Convert.ToString(slPropertyValues[0x001a001f].Value) : slPropertyValues.ContainsKey(0x001a001e) ? Convert.ToString(slPropertyValues[0x001a001e].Value) : "";
                                                                    switch (strClass)
                                                                    {
                                                                        case "IPM.Activity":
                                                                            strType = "jrnl";
                                                                            break;
                                                                        case "IPM.Appointment":
                                                                            strType = "appt";
                                                                            break;
                                                                        case "IPM.Contact":
                                                                            strType = "cntct";
                                                                            break;
                                                                        case "IPM.DistList":
                                                                            strType = "dslst";
                                                                            break;
                                                                        case "IPM.Document":
                                                                            strType = "doc";
                                                                            break;
                                                                        case "IPM.Microsoft.ScheduleData.FreeBusy":
                                                                            strType = "sdfb";
                                                                            break;
                                                                        case "IPM.Note":
                                                                        case "IPM.Note.IMC.Notification":
                                                                        //case "IPM.Note.Rules.Oof.Template.Microsoft":
                                                                        case "IPM.Note.Rules.OofTemplate.Microsoft":
                                                                        case "IPM.Note.Rules.ReplyTemplate.Microsoft":
                                                                        case "IPM.Note.Secure":
                                                                        case "IPM.Note.Secure.Sign":
                                                                        case "IPM.Outlook.Recall":
                                                                        case "IPM.Resend":
                                                                            strType = "email";
                                                                            break;
                                                                        case "IPM.OLE.Class":
                                                                            strType = "rsexc";
                                                                            break;
                                                                        case "IPM.Post":
                                                                            strType = "post";
                                                                            break;
                                                                        case "IPM.Recall.Report":
                                                                        case "IPM.Report":
                                                                            strType = "mrpt";
                                                                            break;
                                                                        case "IPM.Remote":
                                                                            strType = "rmhdr";
                                                                            break;
                                                                        case "IPM.Schedule.Meeting.Canceled":
                                                                        case "IPM.Schedule.Meeting.Request":
                                                                        case "IPM.Schedule.Meeting.Resp.Neg":
                                                                        case "IPM.Schedule.Meeting.Resp.Pos":
                                                                        case "IPM.Schedule.Meeting.Resp.Tent":
                                                                            strType = "mtg";
                                                                            break;
                                                                        case "IPM.StickyNote":
                                                                            strType = "note";
                                                                            break;
                                                                        case "IPM.Task":
                                                                        case "IPM.TaskRequest":
                                                                        case "IPM.TaskRequest.Accept":
                                                                        case "IPM.TaskRequest.Decline":
                                                                        case "IPM.TaskRequest.Update":
                                                                            strType = "task";
                                                                            break;
                                                                        case "IPM":
                                                                            strType = "ntfnd";
                                                                            break;
                                                                        case "":
                                                                            strType = "emcls";
                                                                            break;
                                                                        default:
                                                                            strType = "tbd";
                                                                            break;
                                                                    }
                                                                    string strFile = string.Format("{0}{1:yyyyMMddhhmmsstt}", strType, dtReceived).ToLower();
                                                                    List<int> liChar = new List<int>();
                                                                    liChar.Add(96);
                                                                    mstrCurrentOperation = string.Format("parsing export message filename on {0} in {1} for message 0x{2:x8}; message {3} of {4} in folder {5}", strFile, strExportFolder, u64MessageNIDraw, mintTaskItemsProcessed + 1, mintTaskItemsCount, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                                                    do
                                                                    {
                                                                        int intCharCount = liChar.Count - 1;
                                                                        liChar[intCharCount]++;
                                                                        if (liChar[intCharCount] == 123)
                                                                        {
                                                                            for (int intI = intCharCount; intI >= 0; intI--)
                                                                            {
                                                                                if (liChar[intI] == 123)
                                                                                {
                                                                                    liChar[intI] = 97;
                                                                                    if (intI == 0)
                                                                                    {
                                                                                        liChar.Add(97);
                                                                                        intCharCount = liChar.Count - 1;
                                                                                    }
                                                                                    else
                                                                                    {
                                                                                        liChar[intI - 1]++;
                                                                                    }
                                                                                }
                                                                            }
                                                                        }
                                                                        StringBuilder sbReturn = new StringBuilder();
                                                                        foreach (int intI in liChar)
                                                                        {
                                                                            sbReturn.Append(Convert.ToChar(intI));
                                                                        }
                                                                        strExportFileSpec = string.Format("{0}{1}", strFile, sbReturn.ToString());
                                                                        strExportFile = string.Format("{0}{1}.xml", strExportFolder, strExportFileSpec);
                                                                        fiFile = new FileInfo(strExportFile);
                                                                        sbReturn = null;
                                                                    } while (fiFile.Exists);
                                                                    //Trace.Write(string.Format("File {0} length = {1}", fiFile.Name, fiFile.Name.Length));
                                                                }
                                                                StringBuilder sbPropertyOutput = new StringBuilder();
                                                                XmlWriterSettings xwsProperty = new XmlWriterSettings();
                                                                xwsProperty.ConformanceLevel = ConformanceLevel.Fragment;
                                                                xwsProperty.OmitXmlDeclaration = true;
                                                                XmlWriter xwProperties = XmlWriter.Create(sbPropertyOutput, xwsProperty);
                                                                mstrCurrentOperation = string.Format("writing {0} message properties for {1} from heap 0x{2:x8} in message 0x{3:x8}; message {4} of {5} in folder {6}", slPropertyValues.Values.Count, strExportFileSpec, u32Key, u64MessageNIDraw, mintTaskItemsProcessed + 1, mintTaskItemsCount, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                                                foreach (PropertyValue pvValue in slPropertyValues.Values)
                                                                {
                                                                    try
                                                                    {
                                                                        xwProperties.WriteStartElement(pvValue.Name);
                                                                        if (pvValue.InvalidChars)
                                                                        {
                                                                            xwProperties.WriteRaw(pvValue.XMLValue);
                                                                        }
                                                                        else
                                                                        {
                                                                            xwProperties.WriteString(pvValue.XMLValue);
                                                                        }
                                                                        xwProperties.WriteEndElement();
                                                                    }
                                                                    catch (Exception eException)
                                                                    {
                                                                        mstrCurrentOperation = string.Format("Error:  XML Type Error Exception on NID 0x{0:x8}, Tag 0x{1:x8} {2} {3} - {4}", tneMessageNode.NIDraw, pvValue.Tag, pvValue.Name, pvValue.XMLValue, eException.Message);
                                                                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Exception, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                                                                    }
                                                                }
                                                                xwProperties.Flush();
                                                                xwProperties.Close();
                                                                xwsProperty = null;
                                                                xwProperties = null;
                                                                mstrCurrentOperation = string.Format("adding message properties for {0} from heap 0x{1:x8} in message 0x{2:x8} to XML output array; message {3} of {4} in folder {5}", strExportFileSpec, u32Key, u64MessageNIDraw, mintTaskItemsProcessed + 1, mintTaskItemsCount, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                                                lsbXML.Add(sbPropertyOutput);
                                                            }
                                                            break;
                                                        case NIDTypes.Attachment:
                                                            // Covered and removed in NIDTypes.AttachmentTable
                                                            break;
                                                        case NIDTypes.AttachmentTable:
                                                            if (tneMessageNode.Heaps[u32Key].Context == ContextType.Table)
                                                            {
                                                                mstrCurrentOperation = string.Format("reading attachment table properties for {0} from heap 0x{1:x8} in message 0x{2:x8}; message {3} of {4} in folder {5}", strExportFileSpec, u32Key, u64MessageNIDraw, mintTaskItemsProcessed + 1, mintTaskItemsCount, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                                                SortedList<UInt32, SortedList<UInt32, PropertyValue>> slslAttachmentValues = tneMessageNode.Heaps[u32Key].Table;
                                                                StringBuilder sbAttachmentOutput = new StringBuilder();
                                                                XmlWriterSettings xwsAttachment = new XmlWriterSettings();
                                                                xwsAttachment.ConformanceLevel = ConformanceLevel.Fragment;
                                                                xwsAttachment.OmitXmlDeclaration = true;
                                                                XmlWriter xwAttachments = XmlWriter.Create(sbAttachmentOutput, xwsAttachment);
                                                                xwAttachments.WriteStartElement("Attachments");
                                                                int intI = 1;
                                                                foreach (SortedList<UInt32, PropertyValue> slAttachmentValues in slslAttachmentValues.Values)
                                                                {
                                                                    xwAttachments.WriteStartElement("Attachment");
                                                                    mstrCurrentOperation = string.Format("writing {0} attachment table properties for {1} from heap 0x{2:x8} in message 0x{3:x8}; message {4} of {5} in folder {6}", slAttachmentValues.Values.Count, strExportFileSpec, u32Key, u64MessageNIDraw, mintTaskItemsProcessed + 1, mintTaskItemsCount, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                                                    foreach (PropertyValue pvValue in slAttachmentValues.Values)
                                                                    {
                                                                        xwAttachments.WriteStartElement(pvValue.Name);
                                                                        try
                                                                        {
                                                                            if (pvValue.InvalidChars)
                                                                            {
                                                                                xwAttachments.WriteRaw(pvValue.XMLValue);
                                                                            }
                                                                            else
                                                                            {
                                                                                xwAttachments.WriteString(pvValue.XMLValue);
                                                                            }
                                                                        }
                                                                        catch
                                                                        {
                                                                            if (pvValue.InvalidChars)
                                                                            {
                                                                                xwAttachments.WriteRaw(pvValue.XMLValue);
                                                                            }
                                                                            else
                                                                            {
                                                                                xwAttachments.WriteString(pvValue.XMLValue);
                                                                            }
                                                                        }
                                                                        xwAttachments.WriteEndElement();
                                                                    }
                                                                    if (slAttachmentValues.ContainsKey(0x67f20003))
                                                                    {
                                                                        UInt32 u32AttachmentKey = slAttachmentValues.ContainsKey(0x67f20003) ? Convert.ToUInt32(slAttachmentValues[0x67f20003].Value) : 0;
                                                                        if (tneMessageNode.Heaps.ContainsKey(u32AttachmentKey))
                                                                        {
                                                                            if (tneMessageNode.Heaps[u32AttachmentKey].Context == ContextType.Property)
                                                                            {
                                                                                SortedList<UInt32, PropertyValue> slAttachmentPropertyValues = tneMessageNode.Heaps[u32AttachmentKey].Properties;
                                                                                mstrCurrentOperation = string.Format("writing {0} attachment file properties for {1} from heap 0x{2:x8} in message 0x{3:x8}; message {4} of {5} in folder {6}", slAttachmentPropertyValues.Values.Count, strExportFileSpec, u32AttachmentKey, u64MessageNIDraw, mintTaskItemsProcessed + 1, mintTaskItemsCount, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                                                                foreach (PropertyValue pvValue in slAttachmentPropertyValues.Values)
                                                                                {
                                                                                    xwAttachments.WriteStartElement(pvValue.Name);
                                                                                    if (pvValue.InvalidChars)
                                                                                    {
                                                                                        xwAttachments.WriteRaw(pvValue.XMLValue);
                                                                                    }
                                                                                    else
                                                                                    {
                                                                                        xwAttachments.WriteString(pvValue.XMLValue);
                                                                                    }
                                                                                    xwAttachments.WriteEndElement();
                                                                                }
                                                                                if (slAttachmentPropertyValues.ContainsKey(0x37010102)) // "AttachDataBinary"
                                                                                {
                                                                                    string strAttachmentFileName = string.Format("attachment{0}.{1}", intI, slAttachmentPropertyValues.ContainsKey(0x3703001f) ? slAttachmentPropertyValues[0x3703001f].Value : ".bin");
                                                                                    if (slAttachmentPropertyValues.ContainsKey(0x3707001f))  // "AttachLongFilename"
                                                                                    {
                                                                                        if (slAttachmentPropertyValues[0x3707001f].Value.ToString().Length > 0)
                                                                                        {
                                                                                            strAttachmentFileName = ReplaceForbiddenCharacter(string.Format("{0}", slAttachmentPropertyValues[0x3707001f].Value));
                                                                                        }
                                                                                    }
                                                                                    else if (slAttachmentPropertyValues.ContainsKey(0x3001001f))  // "DisplayName"
                                                                                    {
                                                                                        if (slAttachmentPropertyValues[0x3001001f].Value.ToString().Length > 0)
                                                                                        {
                                                                                            strAttachmentFileName = ReplaceForbiddenCharacter(string.Format("{0}", slAttachmentPropertyValues[0x3001001f].Value));
                                                                                        }
                                                                                    }
                                                                                    else if (slAttachmentPropertyValues.ContainsKey(0x3704001f))  // "AttachLongFilename"
                                                                                    {
                                                                                        if (slAttachmentPropertyValues[0x3704001f].Value.ToString().Length > 0)
                                                                                        {
                                                                                            strAttachmentFileName = ReplaceForbiddenCharacter(string.Format("{0}", slAttachmentPropertyValues[0x3704001f].Value));
                                                                                        }
                                                                                    }
                                                                                    string strAttachmentOutputFileName = string.Format("{0}{1}-att{2}-{3}", strExportFolder, strExportFileSpec, intI, strAttachmentFileName);
                                                                                    FileInfo fiAttachment = new FileInfo(strAttachmentOutputFileName);
                                                                                    if (!fiAttachment.Exists)
                                                                                    {
                                                                                        BinaryWriter bwWriter = new BinaryWriter(fiAttachment.Create());
                                                                                        bwWriter.Write((byte[])slAttachmentPropertyValues[0x37010102].Value);
                                                                                        bwWriter.Flush();
                                                                                        bwWriter.Close();
                                                                                    }
                                                                                }
                                                                            }
                                                                            mstrCurrentOperation = string.Format("removing attachment file heap 0x{0:x8} in {1} from message 0x{2:x8}; message {3} of {4} in folder {5}", u32AttachmentKey, strExportFileSpec, u64MessageNIDraw, mintTaskItemsProcessed + 1, mintTaskItemsCount, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                                                            tneMessageNode.Heaps.Remove(u32AttachmentKey);
                                                                        }
                                                                    }
                                                                    xwAttachments.WriteEndElement();
                                                                    intI++;
                                                                }
                                                                xwAttachments.WriteEndElement();
                                                                xwAttachments.Flush();
                                                                xwAttachments.Close();
                                                                xwsAttachment = null;
                                                                xwAttachments = null;
                                                                mstrCurrentOperation = string.Format("adding attachment properties for {0} from heap 0x{1:x8} in message 0x{2:x8} to XML output array; message {3} of {4} in folder {5}", strExportFileSpec, u32Key, u64MessageNIDraw, mintTaskItemsProcessed + 1, mintTaskItemsCount, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                                                lsbXML.Add(sbAttachmentOutput);
                                                            }
                                                            break;
                                                        case NIDTypes.RecipientTable:
                                                            if (tneMessageNode.Heaps[u32Key].Context == ContextType.Table)
                                                            {
                                                                mstrCurrentOperation = string.Format("reading recipient table properties for {0} from heap 0x{1:x8} in message 0x{2:x8}; message {3} of {4} in folder {5}", strExportFileSpec, u32Key, u64MessageNIDraw, mintTaskItemsProcessed + 1, mintTaskItemsCount, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                                                SortedList<UInt32, SortedList<UInt32, PropertyValue>> slslRecipientValues = tneMessageNode.Heaps[u32Key].Table;
                                                                StringBuilder sbRecipientOutput = new StringBuilder();
                                                                XmlWriterSettings xwsRecipient = new XmlWriterSettings();
                                                                xwsRecipient.ConformanceLevel = ConformanceLevel.Fragment;
                                                                xwsRecipient.OmitXmlDeclaration = true;
                                                                XmlWriter xwRecipients = XmlWriter.Create(sbRecipientOutput, xwsRecipient);
                                                                xwRecipients.WriteStartElement("Recipients");
                                                                foreach (SortedList<UInt32, PropertyValue> slRecipientValues in slslRecipientValues.Values)
                                                                {
                                                                    xwRecipients.WriteStartElement("Recipient");
                                                                    mstrCurrentOperation = string.Format("writing {0} attachment table properties for {1} from heap 0x{2:x8} in message 0x{3:x8}; message {4} of {5} in folder {6}", slRecipientValues.Values.Count, strExportFileSpec, u32Key, u64MessageNIDraw, mintTaskItemsProcessed + 1, mintTaskItemsCount, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                                                    foreach (PropertyValue pvValue in slRecipientValues.Values)
                                                                    {
                                                                        xwRecipients.WriteStartElement(pvValue.Name);
                                                                        try
                                                                        {
                                                                            if (pvValue.InvalidChars)
                                                                            {
                                                                                xwRecipients.WriteRaw(pvValue.XMLValue);
                                                                            }
                                                                            else
                                                                            {
                                                                                xwRecipients.WriteString(pvValue.XMLValue);
                                                                            }
                                                                        }
                                                                        catch
                                                                        {
                                                                            if (pvValue.InvalidChars)
                                                                            {
                                                                                xwRecipients.WriteRaw(pvValue.XMLValue);
                                                                            }
                                                                            else
                                                                            {
                                                                                xwRecipients.WriteString(pvValue.XMLValue);
                                                                            }
                                                                        }
                                                                        xwRecipients.WriteEndElement();
                                                                    }
                                                                    xwRecipients.WriteEndElement();
                                                                }
                                                                xwRecipients.WriteEndElement();
                                                                xwRecipients.Flush();
                                                                xwRecipients.Close();
                                                                xwsRecipient = null;
                                                                xwRecipients = null;
                                                                mstrCurrentOperation = string.Format("adding recipient properties for {0} from heap 0x{1:x8} in message 0x{2:x8} to XML output array; message {3} of {4} in folder {5}", strExportFileSpec, u32Key, u64MessageNIDraw, mintTaskItemsProcessed + 1, mintTaskItemsCount, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                                                lsbXML.Add(sbRecipientOutput);
                                                            }
                                                            break;
                                                        default:
                                                            break;
                                                    }
                                                    mstrCurrentOperation = string.Format("removing heap 0x{0:x8} in {1} from message 0x{2:x8}; message {3} of {4} in folder {5}", u32Key, strExportFileSpec, u64MessageNIDraw, mintTaskItemsProcessed + 1, mintTaskItemsCount, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                                    tneMessageNode.Heaps.Remove(u32Key);
                                                }
                                            }
                                            if (tneMessageNode.Heaps.Count > 0)
                                            {
                                                mstrCurrentOperation = string.Format("Warning:  Heaps left over after processing NID 0x{0:x8}", tneMessageNode.NIDraw);
                                                aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Warning, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                                            }
                                            mstrCurrentOperation = string.Format("clearing lists nulling message node object for {0} on message 0x{1:x8}; message {2} of {3} in folder {4}", strExportFileSpec, u64MessageNIDraw, mintTaskItemsProcessed + 1, mintTaskItemsCount, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                            if (lu32HeapKeys != null)
                                            {
                                                lu32HeapKeys.Clear();
                                                lu32HeapKeys = null;
                                            }
                                            if (tneMessageNode.ByteData != null)
                                            {
                                                tneMessageNode.ByteData.Clear();
                                                tneMessageNode.ByteData = null;
                                            }
                                            if (tneMessageNode.Heaps != null)
                                            {
                                                tneMessageNode.Heaps.Clear();
                                                tneMessageNode.Heaps = null;
                                            }
                                            tneMessageNode = null;

                                            mstrCurrentOperation = string.Format("creating export file {0} for message 0x{1:x8}; message {2} of {3} in folder {4}", strExportFile, u64MessageNIDraw, mintTaskItemsProcessed + 1, mintTaskItemsCount, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                            XmlWriter xwOutput = XmlWriter.Create(strExportFile);
                                            //xwOutput.Formatting = Formatting.Indented;
                                            xwOutput.WriteStartDocument();
                                            xwOutput.WriteStartElement("Message");
                                            mstrCurrentOperation = string.Format("writing {0} XML output segments to {1} for message 0x{2:x8}; message {3} of {4} in folder {5}", lsbXML.Count, strExportFileSpec, u64MessageNIDraw, mintTaskItemsProcessed + 1, mintTaskItemsCount, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                            foreach (StringBuilder sbXML in lsbXML)
                                            {
                                                xwOutput.WriteRaw(sbXML.ToString());
                                            }
                                            xwOutput.WriteEndElement();
                                            xwOutput.WriteEndDocument();
                                            mstrCurrentOperation = string.Format("saving export file {0} for message 0x{1:x8}; message {2} of {3} in folder {4}", strExportFile, u64MessageNIDraw, mintTaskItemsProcessed + 1, mintTaskItemsCount, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                            xwOutput.Close();
                                            if (lsbXML != null)
                                            {
                                                lsbXML.Clear();
                                                lsbXML = null;
                                            }
                                            break;
                                        default:
                                            break;
                                    }
                                    mstrCurrentOperation = string.Format("memory usage {0} after export of file {1} for message 0x{2:x8}; message {3} of {4} in folder {5}", lngCurrentTotalMemory, strExportFileSpec, u64MessageNIDraw, mintTaskItemsProcessed + 1, mintTaskItemsCount, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                    if (lngCurrentTotalMemory > 536870912)
                                    {
                                        mstrCurrentOperation = string.Format("initiating garbage collection after export of file {0} for message 0x{1:x8}; message {2} of {3} in folder {4}", strExportFileSpec, u64MessageNIDraw, mintTaskItemsProcessed + 1, mintTaskItemsCount, fsFolder.Path.Length > 0 ? fsFolder.Path : fsFolder.Name);
                                        aoOperation.Post(this.PostProgress, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                                        GC.Collect();
                                    }
                                }
                                sltneNodeEntries.Remove(u64MessageNIDraw);
                            }
                            if (lu64FoundMessageNIDraws != null)
                            {
                                lu64FoundMessageNIDraws.Clear();
                                lu64FoundMessageNIDraws = null;
                            }
                            fsFolder.Messages.Remove(u64NormalMessageNIDraw);
                            lu64NormalMessageNIDraws.Remove(u64NormalMessageNIDraw);
                            mintTaskItemsProcessed++;
                            mintItemsProcessed++;
                            mintMessagesExported++;
                        }
                        if (lu64MessageKeys != null)
                        {
                            lu64MessageKeys.Clear();
                            lu64MessageKeys = null;
                        }
                        mintFoldersExported++;
                        mintItemsProcessed++;
                    }

                    //bool blnUpdate = (mblnWriteAttachmentBinary && mblnWriteMessageBinary) ? true : (mintItemsProcessed % 5 == 0);

                    //if (blnUpdate)
                    //{
                    //    //peaArgs = new ProgressEventArgs(mintItemsProcessed, mintItemsProcessed, mintFoldersProcessed, mintFoldersExported, mintItemsProcessed, "Progress Status Message needs work", (intItems * 100) / intItems, aoOperation.UserSuppliedState); //olFolder.Name, omiItem.Subject, 
                    //    //aoOperation.Post(this.PostProgress, peaArgs);
                    //}
                }
                catch (System.Exception eException)
                {
                    aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Exception, string.Format("Error:  Execution failure {0} as a result of {1}", mstrCurrentOperation, eException.Message), ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                }
                finally
                {
                    if (fsStream != null)
                    {
                        fsStream.Flush();
                        fsStream.Close();
                        fsStream = null;
                    }
                    if (brReader != null)
                    {
                        brReader.Close();
                        brReader = null;
                    }
                }
            }
        }

        private void CheckHeader(PSTHeader phHeader, long lngLength, AsyncOperation aoOperation)
        {
            if (!TaskCanceled(aoOperation.UserSuppliedState))
            {
                try
                {
                    if (BitConverter.ToString(BitConverter.GetBytes(phHeader.dwMagic)) != "21-42-44-4E")
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), string.Format("dwMagic = {0} instead of 21-42-44-4E", BitConverter.ToString(BitConverter.GetBytes(phHeader.dwMagic))));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Warning, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    if (BitConverter.ToString(BitConverter.GetBytes(phHeader.wMagicClient)) != "53-4D")
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), string.Format("wMagicClient = {0} instead of 53-4D", BitConverter.ToString(BitConverter.GetBytes(phHeader.wMagicClient))));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Warning, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    if (phHeader.wVer != 15 && phHeader.wVer != 23)
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), string.Format("invalid wVer = {0} instead of 15 (ANSI) or 23 (Unicode).  Defaulting to ANSI.", phHeader.wVer));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Warning, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    if (phHeader.wVerClient != 19)
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), string.Format("invalid wVerClient = {0} instead of 19", phHeader.wVerClient));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Warning, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    if (phHeader.bPlatformCreate != 0x01)
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), string.Format("invalid bPlatformCreate = 0x{0:X2} instead of 0x01", phHeader.bPlatformCreate));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Warning, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    if (phHeader.bPlatformAccess != 0x01)
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), string.Format("invalid bPlatformAccess = 0x{0:X2} instead of 0x01", phHeader.bPlatformAccess));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Warning, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    if (BitConverter.ToString(BitConverter.GetBytes(phHeader.dwReserved1OpenDBID)) != "00-00-00-00")
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), string.Format("dwReserved1(OpenDBID) = {0} instead of 00-00-00-00", BitConverter.ToString(BitConverter.GetBytes(phHeader.dwReserved1OpenDBID))));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    if (BitConverter.ToString(BitConverter.GetBytes(phHeader.dwReserved2OpenClaimID)) != "00-00-00-00")
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), string.Format("dwReserved2(OpenClaimID) = {0} instead of 00-00-00-00", BitConverter.ToString(BitConverter.GetBytes(phHeader.dwReserved2OpenClaimID))));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    if (BitConverter.ToString(BitConverter.GetBytes(phHeader.qwUnusedAlign)) != "00-00-00-00-00-00-00-00")
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), string.Format("qwUnused(Align) = {0} instead of 00-00-00-00-00-00-00-00", BitConverter.ToString(BitConverter.GetBytes(phHeader.qwUnusedAlign))));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    if (BitConverter.ToString(BitConverter.GetBytes(phHeader.dwReserved1OpenDBID)) != "00-00-00-00")
                    {
                        // ********* WHY ARE WE DOING THIS AGAIN?????
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), string.Format("dwReserved1(OpenDBID) = {0} instead of 00-00-00-00", BitConverter.ToString(BitConverter.GetBytes(phHeader.dwReserved1OpenDBID))));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    if (BitConverter.ToString(BitConverter.GetBytes(phHeader.dwRootReservedcOrphans)) != "00-00-00-00")
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), string.Format("dwReserved1(OpenDBID) = {0} instead of 00-00-00-00", BitConverter.ToString(BitConverter.GetBytes(phHeader.dwReserved1OpenDBID))));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }


                    //**** root stuff
                    //phHeader.lngFileEOF;
                    //phHeader.ibAllocationMapLast;
                    //phHeader.lngAllocationMapFree;
                    //phHeader.lngPageMapFree;
                    //phHeader.BREFNodeBTbid;
                    //phHeader.BREFNodeBTib;
                    //phHeader.BREFBlockBTbid;
                    //phHeader.BREFBlockBTib;


                    if (phHeader.fAllocationMapValid != 0x02)
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), string.Format("fAllocationMapValid = 0x{0:X2} instead of 0x02", phHeader.fAllocationMapValid));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Warning, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    if (phHeader.bRootReservedbARVec != 0x00)
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), string.Format("bRootReservedbARVec = 0x{0:X2} instead of 0x00", phHeader.bRootReservedbARVec));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    if (BitConverter.ToString(BitConverter.GetBytes(phHeader.wRootReservedcARVec)) != "00-00")
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), string.Format("wRootReservedcARVec = {0} instead of 00-00", BitConverter.ToString(BitConverter.GetBytes(phHeader.wRootReservedcARVec))));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    if ((phHeader.Unicode) && (BitConverter.ToString(BitConverter.GetBytes(phHeader.dwAlign)) != "00-00-00-00"))
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("dwAlign = {0} instead of 00-00-00-00", BitConverter.ToString(BitConverter.GetBytes(phHeader.dwAlign))));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    int intIndex = 0;
                    int intCount = 0;
                    foreach (byte bFreeMap in phHeader.rgbFreeMap)
                    {
                        if (bFreeMap != 0xFF)
                        {
                            intCount++;
                        }
                        intIndex++;
                    }
                    if (intCount > 0)
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), string.Format("{0} of {1} rgbFreeMap entries not set to 0xFF", intCount, intIndex));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    intCount = 0;
                    intIndex = 0;
                    foreach (byte bFreePageMap in phHeader.rgbFreePageMap)
                    {
                        if (bFreePageMap != 0xFF)
                        {
                            intCount++;
                        }
                        intIndex++;
                    }
                    if (intCount > 0)
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), string.Format("{0} of {1} rgbFreePageMap entries not set to 0xFF", intCount, intIndex));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    if (phHeader.bSentinel != 0x80)
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), string.Format("invalid bSentinel = 0x{0:X2} instead of 0x80", phHeader.bSentinel));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Warning, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    if (phHeader.bCryptMethod > 0x02)
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("unrecognized encryption method, assuming unencrypted; bCryptMethod = 0x{0:X2} instead of <0x03", phHeader.bCryptMethod));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Warning, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    if (BitConverter.ToString(BitConverter.GetBytes(phHeader.rgbReserved)) != "00-00")
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), string.Format("rgbReserved = {0} instead of 00-00", BitConverter.ToString(BitConverter.GetBytes(phHeader.rgbReserved))));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    if ((!phHeader.Unicode) && (BitConverter.ToString(BitConverter.GetBytes(phHeader.dwCRCFull)) != "00-00-00-00"))
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), string.Format("ANSI dwReserved = {0} instead of 00-00-00-00", BitConverter.ToString(BitConverter.GetBytes(phHeader.dwCRCFull))));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    if (BitConverter.ToString(phHeader.rgbReserved2) != "00-00-00")
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), string.Format("rgbReserved2 = {0} instead of 00-00-00", BitConverter.ToString(phHeader.rgbReserved2)));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    if (phHeader.bReserved != 0x00)
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), string.Format("invalid bReserved = 0x{0:X2} instead of 0x00", phHeader.bReserved));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                    intCount = 0;
                    intIndex = 0;
                    foreach (byte rgbReserved3 in phHeader.rgbReserved3)
                    {
                        if (rgbReserved3 != 0x00)
                        {
                            intCount++;
                        }
                        intIndex++;
                    }
                    if (intCount > 0)
                    {
                        mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), string.Format("{0} of {1} rgbReserved entries not set to 0x00", intCount, intIndex));
                        aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Debug, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                    }
                }
                catch (System.Exception eException)
                {
                    mstrCurrentOperation = string.Format("Warning:  Error found during {0} as a result of {1}", string.Format("complete check of {0} header information", mstrPSTFilename), eException.Message);
                    aoOperation.Post(this.PostException, new ProgressEventArgs(((mintTaskItemsProcessed * 100) / mintTaskItemsCount), ((mintItemsProcessed * 100) / mintItemsCount), mintMessagesExported, mintFoldersExported, LogEntryType.Exception, mstrCurrentOperation, ((mintItemsProcessed * 100) / mintItemsCount), aoOperation.UserSuppliedState));
                }

            }
        }

        private void CheckMaps(BinaryReader brReader, bool blnUnicode, byte[] rgbFreeMap, byte[] rgbFreePageMap)
        {
            List<PSTMap> lpmAllocationMap = new List<PSTMap>();
            List<PSTMap> lpmPageMap = new List<PSTMap>();
            List<PSTMap> lpmFreeMap = new List<PSTMap>();
            List<PSTMap> lpmFreePageMap = new List<PSTMap>();
            lpmFreeMap.Add(new PSTMap(rgbFreeMap));
            lpmFreePageMap.Add(new PSTMap(rgbFreePageMap));
            int intDataBlockSize;
            int intPageMapFlag = 8;
            int intFreeMapFlag = 16;
            int intFreePageMapFlag = 1024;
            int intAllocationMap = 8;
            int intPageMap = 0;
            int intPageFreePageMap = 0;
            int intFreeMap = 0;
            int intFreePageMap = 0;
            while (!(brReader.PeekChar() == -1))
            {
                lpmAllocationMap.Add(new PSTMap(brReader, PageTypes.AllocationMap, blnUnicode));
                intDataBlockSize = 253440;
                intAllocationMap++;
                if (intAllocationMap >= intPageMapFlag)
                {
                    lpmPageMap.Add(new PSTMap(brReader, PageTypes.PageMap, blnUnicode));
                    intDataBlockSize -= 512;
                    if (intAllocationMap == intPageMapFlag)
                    {
                        intPageMap++;
                        intPageFreePageMap++;
                    }
                    intAllocationMap = 0;
                    if (intPageMap >= intFreeMapFlag)
                    {
                        lpmFreeMap.Add(new PSTMap(brReader, PageTypes.FreeMap, blnUnicode));
                        intDataBlockSize -= 512;
                        intFreeMap++;
                        intPageMap = 0;
                        if (intFreeMapFlag == 16) intFreeMapFlag = 62;
                    }
                    if (intPageFreePageMap >= intFreePageMapFlag)
                    {
                        lpmFreePageMap.Add(new PSTMap(brReader, PageTypes.FreePageMap, blnUnicode));
                        intDataBlockSize -= 512;
                        intFreePageMap++;
                        intPageFreePageMap = 0;
                        if (intFreePageMapFlag == 1024) intFreePageMapFlag = 3968;
                    }
                }
                brReader.BaseStream.Seek(intDataBlockSize, SeekOrigin.Current);
            }
        }

        private SortedList<UInt64, TreeNodeEntry> LoadNodeEntries(PSTBTreePage pbtpPage)
        {
            SortedList<UInt64, TreeNodeEntry> sltneResults = new SortedList<UInt64, TreeNodeEntry>();
            foreach (TreeEntry iPage in pbtpPage.SubEntries)
            {
                if (iPage != null)
                {
                    if (iPage.GetType() == typeof(PSTBTreePage))
                    {
                        SortedList<UInt64, TreeNodeEntry> sltneSubResults = LoadNodeEntries((PSTBTreePage)iPage);
                        foreach (KeyValuePair<UInt64, TreeNodeEntry> kvpEntry in sltneSubResults)
                        {
                            sltneResults.Add(kvpEntry.Key, kvpEntry.Value);
                        }
                    }
                    if (iPage.GetType() == typeof(TreeNodeEntry))
                    {
                        if (!sltneResults.ContainsKey(((TreeNodeEntry)iPage).NIDraw))
                        {
                            sltneResults.Add(((TreeNodeEntry)iPage).NIDraw, (TreeNodeEntry)iPage);
                        }
                        else
                        {
                            Trace.Write(string.Format("Duplicate TreeNodeEntry Found:  NIDraw = 0x{0:x8}; Initial Instance Path = {1} at 0x{2:x8}; Duplicate Path = {3} at 0x{4:x8}", sltneResults[((TreeNodeEntry)iPage).NIDraw].NIDraw, sltneResults[((TreeNodeEntry)iPage).NIDraw].EntryPath, sltneResults[((TreeNodeEntry)iPage).NIDraw].EntryAddress, ((TreeNodeEntry)iPage).EntryPath, ((TreeNodeEntry)iPage).EntryAddress));
                            sltneResults[((TreeNodeEntry)iPage).NIDraw].PathList.Add(((TreeNodeEntry)iPage).EntryPath);
                        }
                    }
                }
            }
            return sltneResults;
        }

        private SortedList<UInt64, TreeBlockEntry> LoadBlockEntries(PSTBTreePage pbtpPage)
        {
            SortedList<UInt64, TreeBlockEntry> sltbeResults = new SortedList<UInt64, TreeBlockEntry>();
            foreach (TreeEntry iPage in pbtpPage.SubEntries)
            {
                if (iPage != null)
                {
                    if (iPage.GetType() == typeof(PSTBTreePage))
                    {
                        SortedList<UInt64, TreeBlockEntry> sltbeSubResults = LoadBlockEntries((PSTBTreePage)iPage);
                        foreach (KeyValuePair<UInt64, TreeBlockEntry> kvpEntry in sltbeSubResults)
                        {
                            sltbeResults.Add(kvpEntry.Key, kvpEntry.Value);
                        }
                    }
                    if (iPage.GetType() == typeof(TreeBlockEntry))
                    {
                        if (!sltbeResults.ContainsKey(((TreeBlockEntry)iPage).BIDraw))
                        {
                            sltbeResults.Add(((TreeBlockEntry)iPage).BIDraw, (TreeBlockEntry)iPage);
                        }
                        else
                        {
                            Trace.Write(string.Format("Duplicate TreeBlockEntry Found:  BIDraw = 0x{0:x8}; Initial Instance Path = {1} at 0x{2:x8}; Duplicate Path = {3} at 0x{4:x8}", sltbeResults[((TreeBlockEntry)iPage).BIDraw].BIDraw, sltbeResults[((TreeBlockEntry)iPage).BIDraw].EntryPath, sltbeResults[((TreeBlockEntry)iPage).BIDraw].EntryAddress, ((TreeBlockEntry)iPage).EntryPath, ((TreeBlockEntry)iPage).EntryAddress));
                            sltbeResults[((TreeBlockEntry)iPage).BIDraw].PathList.Add(((TreeBlockEntry)iPage).EntryPath);
                        }
                    }
                }
            }
            return sltbeResults;
        }

        private void ProcessItem() //MailItem omiItem, string strExportFolder)
        {
        //    int intLoc;
        //    string strExportFile = "";
        //    string strExportFileSpec = "";
        //    Message mItem = new Message();
        //    List<NVP> lmirRecipients = new List<NVP>();
        //    NVP mirRecipient = new NVP();

        //    //*** Do something here

        //    SerializeObject(mItem, strExportFile);
        //    mintItemsProcessed++;
        }

        private byte[] ReadBinaryFile(string strFilename)
        {
            FileStream fsStream = new FileStream(strFilename, FileMode.Open);
            MemoryStream msStream = new MemoryStream();
            try
            {
                BinaryReader brReader = new BinaryReader(fsStream);
                byte[] baBuffer = new byte[50];
                int intReadBytes = 0;

                while ((intReadBytes = brReader.Read(baBuffer, 0, 50)) > 0)
                {
                    msStream.Write(baBuffer, 0, intReadBytes);
                }
            }
            catch
            {
            }
            finally
            {
                fsStream.Close();
                fsStream.Dispose();
            }
            return msStream.ToArray();
        }

        private void SerializeObject(Message mItem, string strFilename)
        {
            XmlSerializer xsSerializer = new XmlSerializer(typeof(Message));
            StreamWriter swWriter = new StreamWriter(strFilename);

            xsSerializer.Serialize(swWriter, mItem);
            swWriter.Close();
        }

        private Guid InitializeAsync()
        {
            Guid gTask = Guid.NewGuid();
            AsyncOperation aoOperation = AsyncOperationManager.CreateOperation(gTask);

            // Multiple threads will access the task dictionary,
            // so it must be locked to serialize access.
            lock (mhdStateDictionary.SyncRoot)
            {
                if (mhdStateDictionary.Contains(gTask))
                {
                    throw new ArgumentException("Task ID parameter must be unique", "gTask");
                }

                mhdStateDictionary[gTask] = aoOperation;
            }
            return gTask;
        }

        private bool TaskCanceled(object gTask)
        {
            return (mhdStateDictionary[gTask] == null);
        }

        private string ReplaceForbiddenCharacter(string strFile)
        {
            strFile = strFile.Replace("/", "&#x2F;");
            strFile = strFile.Replace("\\", "&#x5C;");
            strFile = strFile.Replace(":", "&#x3A;");
            strFile = strFile.Replace("*", "&#x2A;");
            strFile = strFile.Replace("?", "&#x3F;");
            strFile = strFile.Replace("\"", "&#x22;");
            strFile = strFile.Replace("<", "&#x3C;");
            strFile = strFile.Replace(">", "&#x3E;");
            strFile = strFile.Replace("|", "&#x7C;");
            strFile = strFile.Replace("'", "&#x27;");
            strFile = strFile.Trim();
            return strFile;
        }

        #endregion Private Methods
    }

    //internal class ExtractTimer : System.Timers.Timer
    //{
    //    internal ExtractTimer() : base() { }
    //    internal ExtractTimer(double dInterval) : base(dInterval) { }

    //    internal delegate void ExtractTimerElapsedEventHandler(object oSender, ExtractTimerElapsedEventArgs eteeaArgs);
    //    internal event ExtractTimerElapsedEventHandler ExtractTimerElapsed;


    //}

    //internal class ExtractTimerElapsedEventArgs : System.Timers.ElapsedEventArgs
    //{
    //    private object moUserState;

    //    internal ExtractTimerElapsedEventArgs(object oToken) : base() 
    //    {
    //        moUserState = oToken;
    //    }

    //    internal object UserState
    //    {
    //        get { return moUserState; }
    //    }
    //}
}
